import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;

/**
 * 同步非阻塞模型
 *
 * NIO服务端
 *
 */
public class NioServer1 {
    public static void main(String[] args) throws IOException {
        //开启一个服务端socketChannel 底层是 epoll_crate()
        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
        //设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //绑定端口
        serverSocketChannel.bind(new InetSocketAddress(9999));
        //开启selector
        Selector selector=Selector.open();
        //注册事件,将channel注册给selector 底层是 epoll_ctl()
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            //底层 epoll_waite()
            selector.select();
            //就绪的事件信息集合
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                //拿到一个就绪事件
                SelectionKey key = iterator.next();
                //从集合移除当前事件
                iterator.remove();
                //处理请求
                doProcessSelectedKey(key,selector);
            }
        }
    }

    private static void doProcessSelectedKey(SelectionKey key,Selector selector) throws IOException {
        //处理accept事件
        if (key.isAcceptable()) {
            ServerSocketChannel serverSocketChannel=(ServerSocketChannel) key.channel();
            //获取到客户端channel
            SocketChannel socketChannel = serverSocketChannel.accept();
            //设置为非阻塞
            socketChannel.configureBlocking(false);
            //注册新的连接到selector,并且开启读监听器该客户端的读事件
            socketChannel.register(selector, SelectionKey.OP_READ);
            return;
        }
        //read 事件
        if (key.isReadable()) {
            //通过key拿到对应的客户端channel
            SocketChannel socketChannel=(SocketChannel) key.channel();
            ByteBuffer readBuffer=ByteBuffer.allocate(1024);
            //从客户端中读取数据，返回之是字节大小
            int read = socketChannel.read(readBuffer);
            if (read>0) {
                //读写转换
                readBuffer.flip();
                byte[] bytes = new byte[readBuffer.remaining()];
                readBuffer.get(bytes);
                //data decode
                String message = new String(bytes, Charset.defaultCharset());
                System.out.println("收到客户端消息："+message);
                //todo 处理业务

                byte[] bytes1 = "hello nio client, i am nio server \n".getBytes(StandardCharsets.UTF_8);
                ByteBuffer writeBuffer=ByteBuffer.allocate(bytes1.length);
                writeBuffer.put(bytes1);
                writeBuffer.flip();
                socketChannel.write(writeBuffer);
            }
        }
    }
}
